#include <stdlib.h>
#include <string.h>
#include "../types.h"
#include "../../../dRasclib/src/rasclib.h"
#include "rasconnect.h"

#ifdef RASC

/* we need to acces to the triangle memory to extract their vectors */
#define TRIANGLE_PRIVATE
#include "../forms/triangle.h"

/********************************* GLOBALS *******************************/

const t_alg_def  algoritmos[IRRADIA_NUM_OBJS] =
		{
				{IRRADIA_BOX,""},
				{IRRADIA_CYLINDER,""},
				{IRRADIA_EXTVOL,""},
				{IRRADIA_GRID,""},
				{IRRADIA_PLANE,""},
				{IRRADIA_QUADRIC,""},
				{IRRADIA_RING,""},
				{IRRADIA_SPHERE,""},
				{IRRADIA_TRIANGLE,"triang_int"}
		};

t_rasconnect_control rascon_control;
/********************************* LOCAL PROTOTIPES *********************/

int triangle_load(int,object *, int);


/********************************* FUNCTIONS ****************************/

void rcnc_init(){
	memset (&rascon_control, 0x00, sizeof(t_rasconnect_control));
}

int rcnc_monta_entorno (int numFPGAS, int alg_code){

	t_alg_ids *ids;
	char * alg_name;
	int algorithm_id, res;

	alg_name = (char *)algoritmos[alg_code].alg_name;
	if (alg_name[0] == '\0')
		return RASCONNECT_ERROR_NOT_IMPLEMENTED;

	res = rasclib_resource_reserve(numFPGAS, NULL);
	if (res != RASCLIB_SUCCESS) {
		rasclib_perror("reserve", res);
		return RASCONNECT_ERROR_RASCLIB;
	}

	res = rasclib_resource_configure(alg_name, numFPGAS, NULL);
	if (res != RASCLIB_SUCCESS) {

		rasclib_perror("configure", res);
		return RASCONNECT_ERROR_RASCLIB;
	}

	algorithm_id = rasclib_algorithm_open(alg_name, RASCLIB_BUFFERED_IO);
	if (algorithm_id == RASCLIB_FAIL) {

		rasclib_perror("open", res);
		return RASCONNECT_ERROR_RASCLIB;
	}

	if ((ids = malloc(sizeof (t_alg_ids))) == NULL)
		return RASCONNECT_ERROR_MEMORY;

	ids->alg_id = algorithm_id;
	ids->next = rascon_control.ids;
	rascon_control.ids = ids;
	strncpy(ids->name,alg_name,10);

    return algorithm_id;
}

int rcnc_load_objs       (int alg_code, object *obj, int num){

	if (obj == NULL)
		return RASCONNECT_ERROR_MEMORY;

	switch (obj->obj_type){
	case IRRADIA_TRIANGLE:
		return triangle_load(alg_code, obj, num);
	default:
		return RASCONNECT_ERROR_NOT_IMPLEMENTED;
	}
}

int rcnc_load_ray        (int code, ray    *rayo){
	/*
	 * it does not matter which object we intersect, all the rays are loaded in the same way
	 */

	t_mem_ptr *mem_ptr;
	int res;

	/* vector 3* 32 bits -> 96 bits = 12 bytes */
	int *vor;
	int *vdes;

	/** alocate mem */
	vor  = malloc(sizeof(int)*4);
	vdes = malloc(sizeof(int)*4);
	if ((mem_ptr = malloc(sizeof (t_mem_ptr))) == NULL)
			return RASCONNECT_ERROR_MEMORY;
	mem_ptr->ptr = vor;
	mem_ptr->next = rascon_control.mems;
	rascon_control.mems = mem_ptr;
	if ((mem_ptr = malloc(sizeof (t_mem_ptr))) == NULL)
				return RASCONNECT_ERROR_MEMORY;
	mem_ptr->ptr = vdes;
	mem_ptr->next = rascon_control.mems;
	rascon_control.mems = mem_ptr;

	vor[0] = (int)(rayo->o.x * 100000000);
	vor[1] = (int)(rayo->o.y * 100000000);
	vor[2] = (int)(rayo->o.z * 100000000);

	vdes[0] = (int)(rayo->d.x * 10000000);
	vdes[1] = (int)(rayo->d.y * 10000000);
	vdes[2] = (int)(rayo->d.z * 10000000);

	res = rasclib_algorithm_reg_write(code, "or",(unsigned long *)vor, 1, RASCLIB_QCMD);
	if (res != RASCLIB_SUCCESS){
	    return RASCONNECT_ERROR_RASCLIB;
    }
	res = rasclib_algorithm_reg_write(code, "des",(unsigned long *)vdes, 1, RASCLIB_QCMD);
	if (res != RASCLIB_SUCCESS){
	    return RASCONNECT_ERROR_RASCLIB;
    }


	return RASCONNECT_SUCCESS;
}

int rcnc_go           (int alg_code){
	int res;

	res = rasclib_algorithm_go(alg_code);
	if (res != RASCLIB_SUCCESS)
		return RASCONNECT_ERROR_RASCLIB;
	res = rasclib_algorithm_commit(alg_code, NULL);
	if (res != RASCLIB_SUCCESS)
			return RASCONNECT_ERROR_RASCLIB;

	return RASCONNECT_SUCCESS;
}
int rcnc_wait       (int alg_code){
	int res;
	t_mem_ptr *mems;

	res = rasclib_algorithm_wait(alg_code);
	if (res != RASCLIB_SUCCESS)
			return RASCONNECT_ERROR_RASCLIB;

	/* memory areas are not needed anymore */
	mems  = rascon_control.mems;
	while (mems != NULL){
		if (mems->alg_ids == alg_code){
			free (mems->ptr);
			mems->ptr = NULL;
			mems->alg_ids = -15;
		}
		mems = mems->next;
	}

	return RASCONNECT_SUCCESS;
}

int rcnc_set_result_ptr (int alg_code, void *buffer, int num){

	int res;
	/* result is the same as well for any kind of object, one result per object proccessed */

	res = rasclib_algorithm_receive(alg_code, "d_out", buffer,num);
		if (res != RASCLIB_SUCCESS) {
                return RASCONNECT_ERROR_RASCLIB;
    }

	return RASCONNECT_SUCCESS;
}

int rcnc_desmonta_entorno(int alg_code){

	t_alg_ids *ids;

	ids = rascon_control.ids;
	while (ids != NULL){
		if (ids->alg_id == alg_code){
			rasclib_algorithm_close(ids->alg_id);
			ids->alg_id = -15;
			rasclib_resource_return(ids->name, ids->num);
			rasclib_resource_release(ids->num, ids->name);
		}
		ids = ids->next;
	}

	return RASCONNECT_SUCCESS;
}

void rcnc_close_all(){

	t_alg_ids  *ids, *tmp;
	t_mem_ptr  *mem, *tmp2;

	/* unmont devices not done */
	ids = rascon_control.ids;
	while (ids != NULL){
		rcnc_desmonta_entorno(ids->alg_id);
		ids = ids->next;
	}
	/* free mem*/
	mem = rascon_control.mems;
	while (mem != NULL){
		free (mem->ptr);
		mem = mem->next;
	}
	/* free stuctures */
	mem = rascon_control.mems;
	while (mem != NULL){
		tmp2 = mem;
		mem  = mem->next;
		free (tmp2);
	}

	ids = rascon_control.ids;
	while (ids != NULL){
		tmp = ids;
		mem = mem->next;
		free (tmp);
	}

	return RASCONNECT_SUCCESS;
}



/********************** OBject specific loads **************************/

/* THIS CODE IS SPECIFIC FOR THE HDL DESIGN COMPATIBILITY */
int triangle_load(int code,object *objs, int num){

	/*
	 * 3 memory areas,
	 * 128 bits wide each word
	 *
	 * float -> 4 bytes  in IA64
	 * vector -> 96 bits -> 32 free bits each word
	 *
	 */

	int res;
	tri *obj;
	t_mem_ptr *mem_ptr;
	void *A,*B,*C;

	unsigned  long tama;

	/* int is 4 bytes...
	 * float as well
	 */
	int *ptrA, *ptrB, *ptrC;


	tama = num;

	/* allocate memory */
    /* to be free after commit, or wait */
	/*  16 bytes -> 128 bits */

	A = malloc (16*num);
	B = malloc (16*num);
	C = malloc (16*num);
	if ((A == NULL)||(B==NULL)||(C==NULL))
		return RASCONNECT_ERROR_MEMORY;

	if ((mem_ptr = malloc(sizeof (t_mem_ptr))) == NULL)
		return RASCONNECT_ERROR_MEMORY;
	mem_ptr->ptr = A;
	mem_ptr->next = rascon_control.mems;
	rascon_control.mems = mem_ptr;
	if ((mem_ptr = malloc(sizeof (t_mem_ptr))) == NULL)
		return RASCONNECT_ERROR_MEMORY;
	mem_ptr->ptr = B;
	mem_ptr->next = rascon_control.mems;
	rascon_control.mems = mem_ptr;
	if ((mem_ptr = malloc(sizeof (t_mem_ptr))) == NULL)
		return RASCONNECT_ERROR_MEMORY;
	mem_ptr->ptr = C;
	mem_ptr->next = rascon_control.mems;
	rascon_control.mems = mem_ptr;


	ptrA = A;
	ptrB = B;
	ptrC = C;

	obj = (tri*)objs;
	while (obj != NULL){

		/* we multiply floats for 100,000 so they become integers
		 * and we simplify the precision check
		 */

		/* copy first vector in memory A */
		*ptrA     = (int) (obj->v0.x * 100000000);
		*(ptrA+1) = (int) (obj->v0.y * 100000000);
		*(ptrA+2) = (int) (obj->v0.z * 100000000);

		/* copy second vector in memory B */
		*ptrA     = (int) (obj->edge1.x * 100000000);
		*(ptrA+1) = (int) (obj->edge1.y * 100000000);
		*(ptrA+2) = (int) (obj->edge1.z * 100000000);

		/* copy third vector in memory C*/
		*ptrA     = (int) (obj->edge2.x * 100000000);
		*(ptrA+1) = (int) (obj->edge2.y * 100000000);
		*(ptrA+2) = (int) (obj->edge2.z * 100000000);

		/* 32 empty bits in the end of each memory word */
		ptrA += 4;
		ptrB += 4;
		ptrC += 4;

		obj = obj->nextobj;
	}

	/* we have the memory set, now we copy it to the fpga */
	res = rasclib_algorithm_send(code, "a_in", A,
	                                     16 * num);
	if (res != RASCLIB_SUCCESS) {
	                return RASCONNECT_ERROR_RASCLIB;
	}
	res = rasclib_algorithm_send(code, "b_in", B,
	                                     16 * num);
	if (res != RASCLIB_SUCCESS) {
	                return RASCONNECT_ERROR_RASCLIB;
	}
	res = rasclib_algorithm_send(code, "c_in", C,
	                                     16 * num);
	if (res != RASCLIB_SUCCESS) {
	                return RASCONNECT_ERROR_RASCLIB;
	}

	/* and the bound is sent to a register */
	res = rasclib_algorithm_reg_write(code, "tama",&tama, 1, RASCLIB_QCMD);
	if (res != RASCLIB_SUCCESS){
	    	        return RASCONNECT_ERROR_RASCLIB;
	}

	return RASCONNECT_SUCCESS;
}

#endif
